package com.zty.controller;

import com.zty.BO.FileChunkMeta;
import com.zty.BO.MetaFile;
import com.zty.DTO.FileMeta;
import com.zty.VO.BucketVO;
import com.zty.VO.MetaFileVO;
import com.zty.errors.BusinessException;
import com.zty.errors.EnumClientException;
import com.zty.response.CommonResponse;
import com.zty.services.FileService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
 * @Author ZTY
 * @Description
 * @Date 3:50 2023-12-25
 **/

@Validated
@RestController
@RequestMapping("/")
public class FileController {

    private final FileService fileService;

    public FileController(FileService fileService) {
        this.fileService = fileService;
    }


    /**
     * 针对小文件的上传
     * @param bucketName
     * @param file
     * @return  文件的访问路径
     */
    @PostMapping("/upload")
    public CommonResponse<String> upload(@RequestParam("bucketName") @NotBlank(message = "存储桶名不得为空") String bucketName,
                                         @RequestParam("file") @NotNull(message = "文件不得为空") MultipartFile file){
        String fileUrl = fileService.upload(bucketName,file);
        return CommonResponse.success(fileUrl);
    }

    /**
     * 大文件上传要根据文件原始信息先生成元数据信息
     * @param fileMeta
     * @return
     */
    @PostMapping("/meta")
    public CommonResponse<MetaFileVO> meta(@RequestBody @Valid FileMeta fileMeta){
        System.out.println("fileMeta = " + fileMeta);
        MetaFileVO metaFileVO = fileService.meta(fileMeta);
        return CommonResponse.success(metaFileVO);
    }

    /**
     * 生成完元数据信息后，上传大文件切割后的一个小分片
     * @param bucketName 和filename拼接后用于查找文件
     * @param filename  和bucketName拼接后用于查找文件
     * @param md5   用于验证该文件分片的完整性
     * @param chunkNo   标记是第几个分片
     * @param file  分片中的文件内容
     * @return
     */
    @PostMapping("/chunk/upload")
    public CommonResponse<String> chunkUpload(@RequestParam("bucketName") @NotBlank(message = "存储桶名不得为空") String bucketName,
                                              @RequestParam("filename") @NotBlank(message = "文件名不得为空") String filename,
                                              @RequestParam("md5") @NotBlank(message = "文件名md5不得为空") String md5,
                                              @RequestParam("chunkNo") @Min(value = 0,message = "分片序号不得为空") Integer chunkNo,
                                              @RequestParam("file") @NotNull(message = "文件不得为空") MultipartFile file){
        //  上传单个分片就无需返回Url了，有Url也无法直接用于下载文件
        String fileMD5 = fileService.uploadChunk(bucketName,filename,md5,chunkNo,file);
        return CommonResponse.success(fileMD5);
    }


    /**
     * 下载某个数据桶里的某个文件
     * @param response  直接将文件的数据流写入response
     * @param bucketName   存储桶名
     * @param filename  文件名
     */
    @GetMapping("{bucketName}/{filename}")
    public void download(HttpServletResponse response,
                         @PathVariable @NotBlank(message = "存储桶名不得为空") String bucketName,
                         @PathVariable @NotBlank(message = "文件名不得为空") String filename){

        //  先获取到这个文件有哪些分片，以及获取每个分片对应的meta信息
        MetaFile metaFile = fileService.getMeta(bucketName,filename);

        response.reset();
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setContentLength(metaFile.getFilesize().intValue());
        response.setHeader("Content-Dispositon","attachment;filename="+metaFile.getFilename()+"."+metaFile.getExtension());

        //  获取到有哪些分片以及它们对应的meta信息后，要根据meta信息去对应的chunk模块下载文件分片
        for (FileChunkMeta chunk : metaFile.getChunks()) {
            byte[] content = fileService.downloadChunk(chunk);

            //  进行写入
            try {
                //  完成下载后，将其放在response里面，返回
                response.getOutputStream().write(content);
            }catch (Exception e){
                throw new BusinessException(EnumClientException.FAILED_TO_DOWNLOAD_FILE);
            }
        }

    }

    /**
     * 前端部分点击一个桶，就能显示出桶内所有文件的信息
     * 读取文件列表
     * @return
     */
    @GetMapping("/files")
    public CommonResponse<List<BucketVO>> files(){
        List<BucketVO> files = fileService.files();
        return CommonResponse.success(files);
    }

    /**
     * 删除文件
     * @param bucketName
     * @param filename
     * @return
     */
    @DeleteMapping("{bucketName}/{filename}")
    public CommonResponse<Void> delete(@PathVariable @NotBlank(message = "存储桶名不得为空") String bucketName,
                                    @PathVariable @NotBlank(message = "文件名不得为空") String filename){
        fileService.delete(bucketName,filename);
        return CommonResponse.success();
    }

}